#include "testApp.h"
#include "stdio.h"

testApp::testApp(ofxArgs* args){
	this->args = args;
}


Cue * testApp::generateCueFromXml(ofxXmlSettings * XML, int i){
	Cue * newCue;
	float amp = 100;
	int time = XML->getAttribute("CUE","minutes",0.0,i) * 60.0 + XML->getAttribute("CUE","seconds",0.0,i);
	if (strcmp(XML->getAttribute("CUE","type","none",i).c_str(), "random") == 0) {
		newCue = new randomCue(time, XML->getAttribute("CUE","layer",0,i));
		((randomCue*)newCue)->soft = XML->getAttribute("CUE","soft",0,i);
		((randomCue*)newCue)->seldom = XML->getAttribute("CUE","seldom",0,i);
		((randomCue*)newCue)->seldomrate = XML->getAttribute("CUE","seldomrate",1.0,i);
		((randomCue*)newCue)->speed = XML->getAttribute("CUE","speed",1.0,i);
	} else if (strcmp(XML->getAttribute("CUE","type","none",i).c_str(), "duskfile") == 0) {
		newCue = new duskFileCue(time, XML->getAttribute("CUE","layer",0,i));
		((duskFileCue*)newCue)->loop = XML->getAttribute("CUE","loop",0,i);
		((duskFileCue*)newCue)->file = new ofxXmlSettings();
		((duskFileCue*)newCue)->file->loadFile(soundFilePath+folder+XML->getAttribute("CUE","file","",i));
		((duskFileCue*)newCue)->filePath = soundFilePath+folder+XML->getAttribute("CUE","file","",i);
	} else if (strcmp(XML->getAttribute("CUE","type","none",i).c_str(), "acc") == 0) {
		newCue = new accControlCue(time, XML->getAttribute("CUE","layer",0,i));
		((accControlCue*)newCue)->level = &accVolume;
		((accControlCue*)newCue)->masterOn = &masterAccOn;
		((accControlCue*)newCue)->dusk = XML->getAttribute("CUE","dusk",0,i);
		((accControlCue*)newCue)->audio = XML->getAttribute("CUE","audio",0,i);
	} else {
		newCue = new noneCue(time, XML->getAttribute("CUE","layer",0,i));
	}
	newCue->fadein = XML->getAttribute("CUE","fadein",0.0,i);
	newCue->fadeout = XML->getAttribute("CUE","fadeout",0.0,i);
	amp = XML->getAttribute("CUE","amp",100.0,i);
	
	XML->pushTag("CUE",i);
	int numGroups = 0;
	if(XML->getNumTags("GROUPS") > 0){
		XML->pushTag("GROUPS");
		
		//Groups
		numGroups =  XML->getNumTags("GROUP");
		for(int u=0;u<numGroups;u++){
			for(int g=XML->getAttribute("GROUP","begin", 0,u);g<=XML->getAttribute("GROUP","end", 0,u);g++){
				newCue->affects.push_back(g);
			}
		}
		
		XML->popTag();
	}
	if(numGroups == 0){
		for(int g=0;g<64;g++){
			newCue->affects.push_back(g);
		}
	}
	
	//Amps
	int numAmps = 0;
	if(XML->getNumTags("AMPS") > 0){
		XML->pushTag("AMPS");
		numAmps =  XML->getNumTags("AMP");
		for(int u=0;u<numAmps;u++){
			newCue->amps.push_back(Amp(XML->getAttribute("AMP","value", 100.0,u),XML->getAttribute("AMP","time", 0.0 ,u)));
		}
		
		XML->popTag();
	}
	XML->popTag();
	if(numAmps == 0){
		newCue->amps.push_back(Amp(amp));
	}
	return newCue;
}

//--------------------------------------------------------------
void testApp::setup(){
	//	this->args->printArgs();
	//	this->args->printOpts();
	
	debug = false;
	version = "0.1.0";
	cout<<"BLIND v. "+version<<endl<<"-------"<<endl<<endl;
	
	slog("Begin setup");
	for(int i=0;i<40;i++){
		curCue[i] = -1;
	}
	ofSetDataPathRoot("");
	
	if(!settings.loadFile("settings.xml")){
		cout<<"--------"<<endl<<"ERROR: Could not load settings.xml (must be located in the same folder as this application)"<<endl<<"---------"<<endl<<endl;
	}
	settings.pushTag("settings", 0);
	
	
    computerNumber = this->args->getInt(1);
	
	
    cout<<"Computer number: "<<computerNumber<<endl;
	
	
#if defined( TARGET_OSX )
	if(computerNumber == 0)
	computerNumber = 4;
#endif
#if  defined( TARGET_LINUX )
	//	soundFilePath = "/Users/jonas/Documents/Projekter/Teater/Worldripple/BlindSync/lyd/";
#endif
#if  defined( TARGET_WIN32 )
	if(computerNumber == 0)
	computerNumber = 2;
#endif
	
	soundFilePath = settings.getValue("datadir", "/", 0);
	oscControlSender.setup( settings.getValue("monitorip", "localhost", 0), 14000+computerNumber-1);
	
	
	string xmlFile = "timeline.xml";
	
	for(int i=0;i<settings.getNumTags("computer");i++){
		if(settings.getAttribute("computer", "id", 1, i) == computerNumber){
			settings.pushTag("computer", i);
			folder = settings.getValue("datadir", "", 0);
			xmlFile = settings.getValue("xmlfile", "timeline.xml", 0);
			settings.popTag();
		}
	}
	
	
	
	
	cout<<"Start position in sound: "<<(float)60*this->args->getInt(2)/(sound.length/sound.internalFreq)+this->args->getInt(3)/(sound.length/sound.internalFreq)<<"%  -  "<<this->args->getInt(2)<<":"<<this->args->getInt(3)<<endl;
	startTime = (float)60*this->args->getInt(2)/(sound.length/sound.internalFreq)+this->args->getInt(3)/(sound.length/sound.internalFreq);
	cout<<"Connecting to OSC...";
	//Osc
	oscRecv.setup(12001);
	oscSender.setup("localhost", 12000);
	oscRecvTimeline.setup(12002);
	oscSenderTimeline.setup("localhost", 12003);
	
	oscControlRecv.setup(13000+computerNumber-1);
	
    cout<<"Connected."<<endl<<"Loading XML timeline"<<endl;
	
	//XML
	XML.loadFile(soundFilePath+folder+xmlFile);
	soundFile = XML.getValue("SOUNDFILE", "", 0);
	
	if(XML.getNumTags("GPSZONES") > 0){
		XML.pushTag("GPSZONES");
		int numZones = XML.getNumTags("GPSZONE");
		
		for(int i=0;i<numZones;i++){
			gpsZone zone;
			zone.lon = XML.getAttribute("GPSZONE", "lon", 0.0, i);
			zone.lat = XML.getAttribute("GPSZONE", "lat", 0.0, i);
			zone.radius = XML.getAttribute("GPSZONE", "radius", 0.0, i);
			zone.sound.loadSound(soundFilePath+XML.getAttribute("GPSZONE", "sound", "", i));
			zone.sound.stop();
			
			int numZoneCues = XML.getNumTags("CUE");
			for(int i=0;i<numZoneCues;i++){
				zone.cues.push_back(generateCueFromXml(&XML, i));
			}
			
			zones.push_back(zone);
		}
		
		XML.popTag();		
	}
	
	
	if(XML.getNumTags("CUES") > 0){
		XML.pushTag("CUES");
		numCues = XML.getNumTags("CUE");
	}
	cout<<"Loaded timeline "<<soundFilePath+folder+xmlFile<<" with "<<numCues<<" cues"<<endl;
	
	
	if(soundFile.compare("") != 0){
		loadSound(soundFilePath+soundFile);		
	} else {
		cout<<"No sound file given"<<endl;
	}
	
	
	for(int i=0;i<numCues;i++){
		cues.push_back(generateCueFromXml(&XML, i));
	}
	XML.popTag();
	
	
    cout<<"Connecting to arduino"<<endl;
	int arduino = -1;
	
	//Serial
	//serial.enumerateDevices();
#if  defined( TARGET_LINUX )
	serialConnected = serial.setup(0, 115200);
#endif
#if  defined( TARGET_OSX )
	DIR *dir;
	struct dirent *entry;
	dir = opendir("/dev");
	string str = "";
	int deviceCount=0;
	
	if (dir == NULL){
	} else {
		while ((entry = readdir(dir)) != NULL){
			str = (char *)entry->d_name;
			
			if( str.substr(0,12) == "cu.usbserial" ){
				arduino = deviceCount;
			}
			if( str.substr(0,3) == "cu." ){
				deviceCount++;
			}
		}
	}
	
	serialConnected=serial.setup(arduino, 115200);
#endif
#if  defined( TARGET_WIN32 )
	serialConnected=serial.setup(0,115200);
#endif
	if(!serialConnected){
		slog("No arduino found!");
		sound.play();
		sound.setPosition((float)60*this->args->getInt(2)/(sound.length/sound.internalFreq)+this->args->getInt(3)/(sound.length/sound.internalFreq));
		
    } else {
		slog("Connected to arduino "+arduino);
    }
	
	arduinoBufferSize = 0;
	shortSum = -1;
	msgLock = false;
	lstMsg = 0; //last message sent
	lstMsgType = 0; //if last message wasn't a type identifier, type is specified here
	msgTimer2 = msgTimer = 0; //time since last message was sent
	connectedToArduino = false;
	inputTimeline = false;
	intputAccVolume = false;
	intputRandomNoise = false;
	intputSmoothRandomNoise = false;
	intputNone = true;
	lowPass = 0;
	accVolume = 0;
	receivingAcc = false;
	numBytes = 0;
	masterVolume =  1.0;
	masterVibration = 1.0;
	ac1 = 0;
	panOnAcc = false;
	masterAccOn = true;
	
	//Sound
	//	loadSound(soundFilePath+soundFiles[0]);
	//sound.play();
	
	for(int i=0;i<64;i++){
		duskValues[i] = 0;
		duskSendValues[i] = 0;
		duskOutputs[i] = 0;
	}
	
	memset(bytesReturned, 0, 4);
	memset(bytesReturned2, 0, 5);
	
	
	ofSetFrameRate(30);
	
	slog("End setup");
	slog("");
	button = false;
	curTime = 0;
	
	for(int i=0;i<3;i++){
		curAcc0[i] = 0;
		filteredADC[i] = 0;
	}
	
	
	settings.popTag();
}

//--------------------------------------------------------------
void testApp::update(){
	ofxOscMessage m;
	m.setAddress( "/status" );
	m.addIntArg( connectedToArduino );
	m.addIntArg(sound.getIsPlaying());
	m.addFloatArg( (float)sound.getPosition());
	m.addFloatArg( (float)sound.length/sound.internalFreq);
	m.addStringArg(soundFile);
	
	
	int highestCue = -1;
	for(int i=0;i<20;i++){
		if(curCue[i] > highestCue)
		highestCue = curCue[i];
	}
	m.addIntArg(highestCue);
	m.addIntArg(numCues);
	
	m.addFloatArg( filteredADC[0]);
	m.addFloatArg( filteredADC[1]);
	m.addFloatArg( filteredADC[2]);
	m.addFloatArg( accVolume);
	m.addStringArg(version);
	
#if  defined( TARGET_WIN32 )
	SYSTEM_POWER_STATUS batstatus;
	GetSystemPowerStatus(&batstatus);
    m.addIntArg((int)batstatus.BatteryLifePercent);
    m.addIntArg((int)batstatus.BatteryLifeTime);
	
#else
	m.addIntArg(-1);
    m.addIntArg(-1);
#endif
    m.addIntArg(masterAccOn);
	
	for(int i=0;i<4;i++){
		int sum = 0;
		for(int u=0;u<16;u++){
			sum += 100.0*duskValues[u+i*16]/255.0;
		}
	    m.addIntArg(sum/16.0);
	}
	
	oscControlSender.sendMessage(m);
	
	if(ofGetFrameNum() % 100 == 0){
		
		ofxOscMessage m;
		m.setAddress( "/player/acc" );
		m.addIntArg( curAcc0[0]);
		
		m.addIntArg( curAcc0[1]);
		m.addIntArg( curAcc0[2]);
		
		oscSender.sendMessage( m );
		receivingAcc = false;
		
	}
	
	
	
	while(oscControlRecv.hasWaitingMessages()){
		//cout<<"AJSDOIJAS"<<endl;
		
		ofxOscMessage m;
		oscControlRecv.getNextMessage( &m );
		if( strcmp( m.getAddress().c_str(), "/acc/master" ) == 0){
			masterAccOn = m.getArgAsInt32( 0);
		}
		
	}
	
	
	
	if(acc0.size() > 6){
		int lastAcc[3];
		lastAcc[2] = acc0.at(acc0.size() -4);
		lastAcc[1] = acc0.at(acc0.size() -5);
		lastAcc[0] = acc0.at(acc0.size() -6);
		//	level *= 0.8;
		//	level += ((float)fabs(curAcc0[0] - lastAcc[0]) + fabs(curAcc0[1] - lastAcc[1]) + fabs(curAcc0[2] - lastAcc[2]))*0.2;
		
		float lastFilteredAdc[3];
		
		for(int i=0;i<3;i++){
			lastFilteredAdc[i] = filteredADC[i];
			filteredADC[i] = filteredADC[i]-(0.020 * filteredADC[i]) + 0.020 * curAcc0[i];
		}
		
		level = ((float)fabs(fabs(filteredADC[0] - lastFilteredAdc[0]) +  fabs(filteredADC[1] - lastFilteredAdc[1]) + fabs(filteredADC[2] - lastFilteredAdc[2])))*50;
		accVolume += (((float)level/14.0-accVolume)-0.35*2.0)*0.03;
		
		
		if(accVolume > 1.0)
		accVolume = 1.0;
		if(accVolume < 0.0)
		accVolume = 0.0;
		ac1 += (curAcc0[1]/60.0-ac1)*0.02;
		if(panOnAcc)
		sound.setPan(-ac1);
		//	cout<<curAcc0[1]<<endl;
		ofxOscMessage m;
		m.setAddress( "/player/level" );
		m.addFloatArg(accVolume);
		oscSender.sendMessage( m );
	}
	
	
	for(int i=0;i<64;i++){
		duskValues[i] = 0;
	}
	
	
	
	for(int i=0;i<20;i++){
		int bestCue = -1;
		for(int u=0;u<cues.size();u++){
			if(cues[u]->layer == i){
				if(cues[u]->startTime <= curTime){
					if(bestCue == -1){
						bestCue = u;
					} else if(cues[u]->startTime >= cues[bestCue]->startTime){
						bestCue = u;
					}
				}
			}
		}
		
		if(bestCue != -1){
			if(curCue[i] != bestCue){
				cout<<floor(curTime/60.0)<<":"<<floor(curTime-floor(curTime/60.0)*60.0)<<": Cue "<<bestCue<<" on layer "<<i<<" ";
				curCue[i] = bestCue;
				if(cues[bestCue]->type == Cue::duskFile){
					cout<<"type: duskFile, file: "<<((duskFileCue*)cues[bestCue])->filePath<<endl;
				}
				if(cues[bestCue]->type == Cue::random){
					cout<<"type: random. Soft: "<<((randomCue*)cues[bestCue])->soft<<"  seldom: "<<((randomCue*)cues[bestCue])->seldom<<endl;
				}
				if(cues[bestCue]->type == Cue::none){
					cout<<"type: none."<<endl;
				}
				
			}
			cues[bestCue]->doStuff(curTime, &sound, duskValues);
		}
	}
	
	
	
    if(sound.getIsPlaying()){
        curTime = sound.getPosition()*sound.length/(float)sound.internalFreq;
    }
	
	
	//SERIAL
	if(serialConnected){
		bool gotMsg = false;
		unsigned char recv;
		while( serial.available()){
			recv = serial.readByte();
			//			if(recv = serial.readByte() < 0){
			//		connectedToArduino = false;
			//			}
			
			if((int)recv < 0){
				connectedToArduino = false;
			} else {
				numBytes ++;
				bytesReturned2[numBytes-1] = recv;
				
				if(recv == 255){
					numBytes = 0;
				}
				
				if(numBytes == 4){
					gotMsg = true;
					if(!connectedToArduino){
						slog("Arduino connected");
						connectedToArduino = true;
					}
					msgTimer = msgTimer2 = ofGetElapsedTimeMillis();
					if(button != (bool)bytesReturned2[3]){
						if(!button){
							sound.play();
							sound.setPosition((float)60*this->args->getInt(2)/(sound.length/sound.internalFreq)+this->args->getInt(3)/(sound.length/sound.internalFreq));
							cout<<"Play"<<endl;
						} else {
							sound.stop();
							curTime = 0;
							cout<<"Pause"<<endl;
						}
						button = (bool)bytesReturned2[3];
					}
					for(int i=0;i<3;i++){
						curAcc0[i] = bytesReturned2[i];
						acc0.push_back(curAcc0[i]);
						
					}
				}
			}
		}
		
		
		if(gotMsg){
			unsigned char send[65];
			for(int i=0;i<64;i++){
				if(duskValues[i] > 254)
				duskValues[i] = 254;
				send[i] = duskValues[i];
			}
			send[64] = 255;
			serial.writeBytes(send, 65);
		}
		
		if(ofGetElapsedTimeMillis() - msgTimer > MSG_TIME_OUT){
			connectedToArduino = false;
			receivingAcc =false;
			msgQueue.clear();
			//			setOutputShort(duskValues);
			serial.writeByte(0);
			
			
			msgTimer = ofGetElapsedTimeMillis();
			
			
		}
		
		
	};
	
	if(ofGetElapsedTimeMillis() - msgTimer2 > MSG_TIME_OUT*10){
		serial.close();
		int arduino = -1;
#if  defined( TARGET_OSX )
		DIR *dir;
		struct dirent *entry;
		dir = opendir("/dev");
		string str = "";
		int deviceCount=0;
		
		if (dir == NULL){
		} else {
			while ((entry = readdir(dir)) != NULL){
				str = (char *)entry->d_name;
				
				if( str.substr(0,12) == "cu.usbserial" ){
					arduino = deviceCount;
				}
				if( str.substr(0,3) == "cu." ){
					deviceCount++;
				}
			}
		}
		if(arduino != -1)
		serialConnected=serial.setup(arduino, 115200);
#endif
#if  defined( TARGET_WIN32 )
		serialConnected=serial.setup(0,115200);
#endif
		//	cout<<"Reconnect "<<serialConnected<<endl;
		msgTimer2 = ofGetElapsedTimeMillis();
		
	}
}

void testApp::slog(string s, bool end){
	if(end){
		cout<<s<<endl;
		logString += s+"\n";
	}
	else {
		cout<<s;
		logString += s;
	}
}

void testApp::loadSound(string file){
	slog("Load sound: \"	"+file+"\"...", true);
	sound.loadSound(file);
	soundLoaded = sound.bLoadedOk;
	if(soundLoaded)
	slog("Sound loaded. Length: "+ofToString((sound.length/sound.internalFreq),0)+" sec, freq: "+ofToString(sound.internalFreq,0));
	else
	slog("Sound could not be loaded!");
	sound.stop();
	
}
